package com.common.atom.seal.util;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import javax.script.ScriptException;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Map;
import java.util.Properties;

/**
 * description: SealTemplateDrawUtils <br>
 * date: 2023/4/14  <br>
 * author: zss <br>
 * version: 1.0.0 <br>
 */
public class SealTemplateDrawUtils {

    /**
     * 获取印章模板
     *
     * @param sealTypeName
     * @return
     * @throws Exception
     */
    public static JSONObject getSealTemplateByTypeName(String sealTypeName) throws Exception {
        JSONObject returnObj = null;
        InputStream inputStream = SealTemplateDrawUtils.class.getResourceAsStream("/seal-template-rule.json");
        byte[] bytesJson = IOUtils.toByteArray(inputStream);

        JSONArray jsonArray = new JSONArray(new String(bytesJson, "UTF-8"));
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject jsonSealTemplate = jsonArray.getJSONObject(i);
            String templateName = jsonSealTemplate.getString("name");
            if (templateName.equals(sealTypeName)) {
                returnObj = jsonSealTemplate;
                break;
            }
        }
        return returnObj;
    }

    public static BufferedImage drawSeal(BufferedImage bufferedImage, JSONObject templateRule,
                                         Map<String, Object> parameters, Properties fontTable, double alpha)
            throws ParseException, ScriptException, JSONException, UnsupportedEncodingException {
        int radiusX = templateRule.getIntValue("radiusX");
        int radiusY = templateRule.getIntValue("radiusY");
        String name = templateRule.getString("name");
        String content1 = (String) parameters.get("content1");
        String content2 = (String) parameters.get("content2");
        String content3 = (String) parameters.get("content3");
        int fontType = Integer.parseInt(parameters.get("fontType").toString());

        // String taxSerialNumber = parameters.get("taxSerialNumber");
        // String fanTextBottom = parameters.get("fanTextBottom");
        Color inkpadColor = createColor(String.valueOf(parameters.get("color")), alpha);// 印泥颜色


        if (bufferedImage == null) {
            bufferedImage = SealUtils.createTransparentBufferedImage(radiusX == 0 ? 1 : radiusX * 2,
                    radiusY == 0 ? 1 : radiusY * 2);
        } else {
            radiusX = bufferedImage.getWidth();
            radiusY = bufferedImage.getHeight();
        }
        Graphics2D graphics2d = getSmoothGraphics2D(bufferedImage);
        int circleBorder = 8;// 外圆边框
        int offset = 10;// 底边字与内边最小距离
        FontMetrics metrics = graphics2d.getFontMetrics();
        JSONArray attachedObjects = templateRule.getJSONArray("attachedObjects");
        String orgName = (String) content1;
        int horizontalTextY;


        for (int i = 0; i < attachedObjects.size(); i++) {
            JSONObject attachedObject = attachedObjects.getJSONObject(i);
            String attachedType = attachedObject.getString("attachedType");
            String fontFamily = attachedObject.getString("fontFamily");
            int fontSize = 30;
            String fontWeight = attachedObject.getString("fontWeight");

            Font f = null;
            if (fontType == 1) {
                f = new Font("Microsoft YaHei", Font.BOLD, fontSize);
            } else if (fontType == 2) {
                f = new Font("SimHei", Font.BOLD, fontSize);
            } else if (fontType == 3) {
                f = new Font("SimSun", Font.BOLD, fontSize);
            } else if (fontType == 4) {
                f = new Font("华文行楷", Font.BOLD, fontSize);
            } else if (fontType == 5) {
                f = new Font("方正舒体", Font.BOLD, fontSize);
            } else {
                f = new Font("Microsoft YaHei", Font.BOLD, fontSize);
            }

            if ("TopText".equals(attachedType)) {// 画上弧文字
                int fontStyle = "bold".equals(fontWeight) ? 1 : 0;
                double totalArcAng;
                Font fontTextTop = f;
                if (orgName.length() <= 14) {
                    totalArcAng = 180;
//					fontTextTop = new Font("宋体", Font.BOLD, 28);
                } else {
                    totalArcAng = attachedObject.getDoubleValue("totalArcAng");
                    fontStyle = attachedObject.getBooleanValue("fontItalic") ? fontStyle | 2 : fontStyle;
//					fontTextTop = new Font(fontFamily, fontStyle, fontSize);
                }
                int a = fontTextTop.getSize();
                int b = fontTextTop.getSize();
                double xscaleOfTextTop = attachedObject.getDoubleValue("xscaleOfTextTop");
                double yscaleOfTextTop = attachedObject.getDoubleValue("yscaleOfTextTop");
                graphics2d.setColor(inkpadColor);
                drawBorderedRoundOval(graphics2d, radiusX, radiusY, circleBorder);
                fontTextTop = scaleFont(fontTextTop, xscaleOfTextTop, yscaleOfTextTop);
                drawTopText(graphics2d, radiusX, radiusY, orgName, fontTextTop, totalArcAng, a, b, offset,
                        circleBorder);
            } else if ("TextBottom".equals(attachedType)) { // 画下弧文字
                String fanTextBottom = content3;// "d0a81a1e-4124-9d9f4e9e8a65".toUpperCase();
                if (!StringUtils.isBlank(fanTextBottom)) {
                    int fontStyle = "plain".equals(fontWeight) ? 1 : 0;
                    fontStyle = attachedObject.getBooleanValue("fontItalic") ? fontStyle | 2 : fontStyle;
//					Font fontTextBottom = new Font(fontFamily, fontStyle, fontSize);
                    Font fontTextBottom = f;
                    double angleOfFanBottom = attachedObject.getDoubleValue("angleOfFanBottom");

                    drawBottomText(graphics2d, radiusX, radiusY, fanTextBottom, fontTextBottom, angleOfFanBottom,
                            offset + circleBorder + metrics.getDescent(), offset + circleBorder + metrics.getDescent());
                }

            } else if ("HorizontalText".equals(attachedType)) { // 财务专用章
                String horizontalText = content2;
                int fontStyle = "bold".equals(fontWeight) ? 1 : 0;
                fontStyle = attachedObject.getBooleanValue("fontItalic") ? fontStyle | 2 : fontStyle;
//				Font horizontalTextFont = new Font(fontFamily, fontStyle, fontSize);
                Font horizontalTextFont = f;

                int maxLength = 6;
                int textLength = horizontalText.length();
                if (textLength > maxLength) {
                    horizontalTextFont = scaleFont(horizontalTextFont, (float) maxLength / textLength, 1.8);
                } else {
                    horizontalTextFont = scaleFont(horizontalTextFont, 1, 1.8);
                }

                FontMetrics fontMetrics1 = createFontMetrics(horizontalTextFont);
                int i1 = fontMetrics1.stringWidth(horizontalText);
                horizontalTextY = radiusY + (fontMetrics1.getHeight() / 2 - fontMetrics1.getDescent());
                drawHorizontalCenteredText(graphics2d, radiusX - i1 / 2, horizontalTextY + 40, horizontalText,
                        horizontalTextFont); // 位移偏差
            }

        }
        graphics2d.dispose();
        return bufferedImage;
    }

    public static Color createColor(String colorStr, double alphax) throws UnsupportedEncodingException {
        Color color = null;

        int alpha = (int) Math.round(255 * alphax / 10);
        if (colorStr != null) {
            if (colorStr.startsWith("#")) {
                colorStr = colorStr.substring(1);

                byte fontR = HexUtils.decode(colorStr.substring(0, 2).getBytes(StandardCharsets.UTF_8))[0];
                int fontR1 = fontR < 0 ? fontR + 256 : fontR;
                byte fontG = HexUtils.decode(colorStr.substring(2, 4).getBytes(StandardCharsets.UTF_8))[0];
                int fontG1 = fontG < 0 ? fontG + 256 : fontG;
                byte fontB = HexUtils.decode(colorStr.substring(4, 6).getBytes(StandardCharsets.UTF_8))[0];
                int fontB1 = fontB < 0 ? fontB + 256 : fontB;
                color = new Color(fontR1, fontG1, fontB1, alpha);
            } else {
                switch (colorStr) {
                    case "1":// 红色
                        color = new Color(230, 0, 18, alpha); // 设置画笔颜色
                        break;
                    case "2":// 绿色
                        color = new Color(18, 230, 18, alpha); // 设置画笔颜色
                        break;
                    case "3":// 蓝色
                        color = new Color(18, 0, 230, alpha); // 设置画笔颜色
                        break;
                    case "4":// 黑色
                        color = new Color(0, 0, 0, alpha); // 设置画笔颜色
                        break;
                    default:
                        color = new Color(230, 0, 18, alpha); // 设置画笔颜色
                        break;
                }
            }
        }

        return color;
    }


    /**
     * 取得 bufferedImage 的 Graphics2D对象，并设置绘制图形为光滑模式
     *
     * @param bufferedImage
     */
    public static Graphics2D getSmoothGraphics2D(BufferedImage bufferedImage) {
        Graphics2D graphics2d = (Graphics2D) bufferedImage.getGraphics();
        graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        return graphics2d;
    }

    /**
     * 在指定区域内绘制具有边宽的椭圆形
     *
     * @param graphics2d
     * @param radiusX
     * @param radiusY
     * @param border
     */
    public static void drawBorderedRoundOval(Graphics2D graphics2d, int radiusX, int radiusY, int border) {
        graphics2d.setStroke(new BasicStroke(border));
        graphics2d.drawOval(border, border, (radiusX - border) * 2, (radiusY - border) * 2);
    }

    /**
     * 按照指定的比例变换字体
     *
     * @param font
     * @param xscale
     * @param yscale
     */
    public static Font scaleFont(Font font, double xscale, double yscale) {
        AffineTransform scaleTransform = new AffineTransform();
        scaleTransform.scale(xscale, yscale);
        return font.deriveFont(scaleTransform);
    }

    /**
     * 画上弧上的文字
     *
     * @param graphics2d
     * @param radiusX     长半轴
     * @param radiusY     短半轴
     * @param companyName 公司名字
     * @param font        公司名字字体
     * @param totalArcAng 文字总的跨弧角度
     * @param a           文字与外部的距离
     * @param b           文字与外部的距离
     */
    public static void drawTopText(Graphics2D graphics2d, int radiusX, int radiusY, String companyName, Font font,
                                   double totalArcAng, int a, int b, int border, int circleborder) {
        double startAng = 90F + totalArcAng / 2f;
        int count = companyName.length();
        int w = radiusX - a - border - circleborder - 20;
        int d = radiusY - b - border - circleborder - 20;
        char[] texts = companyName.toCharArray();
        // 算出每一个字的位置
        int maxLength = 15;
        int textLength = companyName.length();
        for (int i = 0; i < count; i++) {
            java.util.List<Double> list = rerturnAngleList(count, totalArcAng, startAng, w, d, true);
            Point point = returnPoint(list.get(i), w, d);

            double α = Obliquity(point, w, d);
            graphics2d.setFont(font);
            FontMetrics metrics = graphics2d.getFontMetrics();
            Point p = returnNewPosition(point, metrics, texts[i], α, true);

            AffineTransform transform;
            if (point.getY() < 0) {
                transform = AffineTransform.getRotateInstance(Math.PI - α);
            } else {
                transform = AffineTransform.getRotateInstance(-α);
            }

            if (textLength > maxLength) {
                transform.scale((double) maxLength / textLength, 1.6);
            } else {
                transform.scale(1, 1.6);
            }

            Font thisFont = font.deriveFont(transform);
            graphics2d.setFont(thisFont);
            graphics2d.drawString(String.valueOf(texts[i]), (int) (p.x + radiusX), (int) (-p.y + radiusY));

        }

    }

    /**
     * 画下弧的字
     *
     * @param graphics2d  画笔
     * @param radiusX     长半轴
     * @param radiusY     短半轴
     * @param text        文字
     * @param font        字体
     * @param totalArcAng 总角度
     * @param a           内部参考圆长半轴与外弧的距离
     * @param b           内部参考圆短半轴与外弧的距离
     */
    public static void drawBottomText(Graphics2D graphics2d, int radiusX, int radiusY, String text, Font font,
                                      double totalArcAng, int a, int b) {
        double startAng = -90 - totalArcAng / 2f;
        int count = text.length();
        int w = radiusX - a;
        int d = radiusY - b;
        char[] texts = text.toCharArray();
        // 算出每一个字的位置
        int maxLength = 9;
        int textLength = text.length();
        for (int i = 0; i < count; i++) {
            java.util.List<Double> list = rerturnAngleList(count, totalArcAng, startAng, w, d, false);
            Point point = returnPoint(list.get(i), w, d);

            double α = Obliquity(point, w, d);
            graphics2d.setFont(font);
            FontMetrics metrics = graphics2d.getFontMetrics();
            Point p = returnNewPosition(point, metrics, texts[i], α, false);
            AffineTransform transform;
            transform = AffineTransform.getRotateInstance(-α);

            if (textLength > maxLength) {
                transform.scale((float) maxLength / textLength, 1.2);
            } else {
                transform.scale(1, 1.2);
            }

            Font thisFont = font.deriveFont(transform);

            graphics2d.setFont(thisFont);
            graphics2d.drawString(String.valueOf(texts[i]), (int) (p.x + radiusX), (int) (-p.y + radiusY));

        }
    }

    /**
     * 输入字符数，得到角度的弧度列表
     *
     * @param count       字数
     * @param totalArcAng 弧长所跨的总角度
     * @param startAng    起始角度
     * @param w           长半轴
     * @param d           短半轴
     * @return
     */
    public static java.util.List<Double> rerturnAngleList(int count, double totalArcAng, double startAng, int w, int d,
                                                          boolean top) {
        java.util.List<Double> list = new ArrayList<Double>();
        double step = top ? 0.5 : -0.5;
        int alCount = (int) Math.ceil(totalArcAng / Math.abs(step)) + 1;
        double[] angArr = new double[alCount];
        double[] arcLenArr = new double[alCount];
        int num = 0;
        double accArcLen = 0.0;
        angArr[num] = startAng;
        arcLenArr[num] = accArcLen;
        num++;
        Point point = returnPoint(startAng, w, d);
        double lastX = point.getX();
        double lastY = point.getY();
        for (double i = startAng - step; num < alCount; i -= step) {
            Point p = returnPoint(i, w, d);
            double x = p.getX();
            double y = p.getY();
            accArcLen += Math.sqrt((lastX - x) * (lastX - x) + (lastY - y) * (lastY - y));
            angArr[num] = i;
            arcLenArr[num] = accArcLen;
            lastX = x;
            lastY = y;
            num++;
        }

        double arcPer = accArcLen / count;
        for (int i = 0; i < count; i++) {
            double arcL = (count - i - 1) * arcPer + arcPer / 2.0;
            double ang = 0.0;
            for (int p = 0; p < arcLenArr.length - 1; p++) {
                if (arcLenArr[p] <= arcL && arcL <= arcLenArr[p + 1]) {
                    ang = (arcL >= ((arcLenArr[p] + arcLenArr[p + 1]) / 2.0)) ? angArr[arcLenArr.length - p - 1]
                            : angArr[arcLenArr.length - p];
                    break;
                }
            }
            list.add(ang);
            // .out.println("----ang----:" + i + "--" + ang);
        }
        return list;
    }

    /**
     * 输入一个角度，得到以中心坐标为原点表示的椭圆上的点
     *
     * @param angle 角度
     * @param w     长半轴
     * @param d     短半轴
     */
    public static Point returnPoint(double angle, int w, int d) {
        Point point = new Point();
        double α = Math.toRadians(angle);
        if (angle <= 180 && angle >= 0) {
            point.y = (int) Math.sqrt((Math.pow(w, 2) * Math.pow(d, 2)
                    / (Math.pow(w, 2) - Math.pow(d, 2) * (1 - 1 / Math.pow(Math.sin(α), 2)))));
        } else {
            point.y = -(int) Math.sqrt((Math.pow(w, 2) * Math.pow(d, 2)
                    / (Math.pow(w, 2) - Math.pow(d, 2) * (1 - 1 / Math.pow(Math.sin(α), 2)))));
        }
        if (angle <= 90 && angle >= -90) {
            point.x = (int) Math.sqrt((Math.pow(w, 2) * Math.pow(d, 2)
                    / (Math.pow(d, 2) - Math.pow(w, 2) * (1 - 1 / Math.pow(Math.cos(α), 2)))));
        } else {
            point.x = -(int) Math.sqrt((Math.pow(w, 2) * Math.pow(d, 2)
                    / (Math.pow(d, 2) - Math.pow(w, 2) * (1 - 1 / Math.pow(Math.cos(α), 2)))));
        }
        return point;
    }

    /**
     * 写字时候文字的定位
     *
     * @param point   切点
     * @param metrics
     * @param ch
     * @param α       旋转的角度
     * @param top     是不是上弧？
     */
    public static Point returnNewPosition(Point point, FontMetrics metrics, char ch, double α, boolean top) {
        Point p = new Point();
        if (top) {
            if (point.getY() < 0 && point.getX() > 0) {
                p.x = (int) (point.x + metrics.charWidth(ch) / 2.0 * Math.cos(α));
                p.y = (int) (point.y + metrics.charWidth(ch) / 2.0 * Math.sin(α));
            } else if (point.getY() < 0 && point.getX() > 0) {
                p.x = (int) (point.x + metrics.charWidth(ch) / 2.0 * Math.cos(α));
                p.y = (int) (point.y - metrics.charWidth(ch) / 2.0 * Math.sin(α));
            } else if (point.getY() < 0 && point.getX() < 0) {
                p.x = (int) (point.x + metrics.charWidth(ch) / 2.0 * Math.cos(α));
                p.y = (int) (point.y + metrics.charWidth(ch) / 2.0 * Math.sin(α));
            } else {
                p.x = (int) (point.x - metrics.charWidth(ch) / 2.0 * Math.cos(α));
                p.y = (int) (point.y - metrics.charWidth(ch) / 2.0 * Math.sin(α));
            }
        } else {
            p.x = (int) (point.x - metrics.charWidth(ch) / 2.0 * Math.cos(α));
            p.y = (int) (point.y - metrics.charWidth(ch) / 2.0 * Math.sin(α));
        }
        return p;
    }

    /**
     * 由椭圆上点的点得到该点切线的倾角
     *
     * @param point 椭圆上点的坐标
     */
    public static double Obliquity(Point point, int w, int d) {
        double k = 0;
        double α = 0;
        double x = point.getX();
        double y = point.getY();
        k = -Math.pow(d, 2) * x / Math.pow(w, 2) / y;// 某一点的切线倾角
        α = Math.atan(k);// 弧度，k<0该切线倾角大于-π/2，小于0,若k>0该切线倾角d大于0小于π/2
        return α;
    }

    /**
     * 根据给定的字体和变形参数返回 FontMetrics 对象，用于动态计算文字位置
     *
     * @param font
     */
    public static FontMetrics createFontMetrics(Font font) {
        // 构造一个1x1像素的 BufferedImage
        BufferedImage bufferedIamge = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
        Graphics2D graphics2d = getSmoothGraphics2D(bufferedIamge);
        graphics2d.setFont(font);
        return graphics2d.getFontMetrics();
    }

    /**
     * 指定Y轴坐标，水平居中绘制文字。注意：(0,0)位于画布左上角，Y轴方向向下
     *
     * @param graphics2d
     * @param radiusX
     * @param text
     * @param textFont
     * @param textY
     */
    public static void drawHorizontalCenteredText(Graphics2D graphics2d, int radiusX, int textY, String text,
                                                  Font textFont) {
        graphics2d.setFont(textFont);
        graphics2d.drawString(text, radiusX, textY);
    }
}
